%% @doc テンプレート処理系のフロントエンド
%%
-module(templ).
-export([start/0]).
-export([init/0,init/1]).
-export([process/1, process/2, process/4]).
-export([process_file/1, process_file/2, process_file/4]).
-export([set_path/1, append_path/1]).

-import(templ_scan, [string/1]).
-import(templ_parse, [parse/1]).

%% @type templ() = string()
%% @type context() = [{string(),term()}]
%% @type delim() = string()


%% @spec init_pman() -> true | none
%% @doc templ_path_managerの初期化処理を行う
%%
init_pman() ->
	case whereis(pman) of
		undefined ->
			{ok, P} = templ_path_manager:start(["."], []),
			register(pman, P);
		_ -> none
	end.


%% @spec init(context()) -> ok
%% @doc templの初期化処理を行う
%%
init(List) when is_list(List) ->
	init_pman(),
	templ_dict:init(List).

init() -> init([]).

start() -> init().


%% @spec process(templ()) -> templ()
%% @doc デフォルトのコンテキストとデリミタに基づき、テンプレート処理を行う
%%
process(Templ) when is_list(Templ) ->
	templ_dict:reset_stmt(),
	{ok,_} = parse(element(2, string(Templ)) ++ [{'$end',999}]),
	Stmt = templ_dict:get_stmt(),
	[S] = Stmt, % 要素数は1のはず
	templ_eval:eval_stmts(S, templ_eval:halt_cont());
process(Templ) ->
	erlang:error({badarg,Templ}).


%% @spec process(templ(), context()) -> templ()
%% @doc 引数のコンテキストとデフォルトのデリミタに基づき、
%%   テンプレート処理を行う
%%
process(Templ, Ctx) when is_list(Templ), is_list(Ctx) ->
	% TODO stacking
	init(Ctx),
	process(Templ);
process(Templ, Ctx) ->
	erlang:error({badarg,[Templ,Ctx]}).


%% @spec process(templ(), context(), delim(), delim()) -> templ()
%% @doc 引数のコンテキストとデリミタに基づき、テンプレート処理を行う
%%
process(Templ, Ctx, LDelim, RDelim) 
  when is_list(Templ), is_list(Ctx), is_list(LDelim), is_list(RDelim) ->
	init(Ctx),
	templ_dict:set_delim(LDelim, RDelim),
	process(Templ);
process(Templ, Ctx, LDelim, RDelim) 
  when is_list(Templ), is_list(Ctx), is_number(LDelim), is_number(RDelim) ->
	process(Templ, Ctx, [LDelim], [RDelim]);
process(Templ, Ctx, LDelim, RDelim) ->
	erlang:error({badarg,[Templ,Ctx,LDelim,RDelim]}).


%% @spec process_file(file()) -> templ()
%% @doc デフォルトのコンテキストとデリミタに基づき、
%% 引数のファイルのテンプレート処理を行う
%%
process_file(File) ->
	Templ = read_file(File),
	process(Templ).

%% @spec process_file(file(), context()) -> templ()
%% @doc 引数のコンテキストとデフォルトのデリミタに基づき、
%% 引数のファイルのテンプレート処理を行う
%%
process_file(File, Ctx) ->
	Templ = read_file(File),
	process(Templ, Ctx).

%% @spec process_file(file(), context(), delim(), delim()) -> templ()
%% @doc 引数のコンテキストとデリミタに基づき、
%% 引数のファイルのテンプレート処理を行う
%%
process_file(File, Ctx, LDelim, RDelim) ->
	Templ = read_file(File),
	process(Templ, Ctx, LDelim, RDelim).


read_file(File) ->
	Path = case templ_path_manager:get_path(pman) of
			   [] -> 
				   {ok,Dir} = file:get_cwd(), 
				   [Dir];
			   P -> P
		   end,
	read_file(File, Path).

read_file(File, [Dir|Dirs]) ->
	case file:read_file(Dir++[$/]++File) of
		{ok, Bin} -> binary_to_list(Bin);
		{error, _Why} -> read_file(File, Dirs)
	end;
read_file(File, []) ->
	erlang:error({filenotfound,File}).


%% @spec set_path([string()] | string()) -> ok
%% @doc テンプレートファイル読み込みパスを上書き設定する
%%
set_path(Path) ->
	templ_path_manager:set_path(pman, Path).
	
%% @spec append_path([string()] | string()) -> ok
%% @doc テンプレートファイル読み込みパスを追加設定する
%%
append_path(Path) ->
	templ_path_manager:append_path(pman, Path).
